package org.foreningsgatan.mercenaries.rendering;

import java.util.ArrayList;
import java.util.List;

import org.foreningsgatan.mercenaries.Configuration;
import org.foreningsgatan.mercenaries.model.LightSource;
import org.foreningsgatan.mercenaries.model.Vegetation;
import org.foreningsgatan.mercenaries.rendering.cache.VegetationCache;
import org.foreningsgatan.mercenaries.weather.WindController;
import org.foreningsgatan.modular.common.logging.Log;
import org.foreningsgatan.modular.common.performance.Timer;
import org.foreningsgatan.modular.lightsources.LightMap;
import org.foreningsgatan.modular.lightsources.LightSources;
import org.foreningsgatan.modular.lightsources.RGB;
import org.foreningsgatan.modular.shadows.generator.ShadowGenerator;
import org.foreningsgatan.modular.shadows.generator.ShadowGenerator.Modes;
import org.foreningsgatan.modular.shadows.model.Shadow;
import org.foreningsgatan.modules.glcore.frames.Frames;
import org.foreningsgatan.modules.glcore.graphics.Alignment;
import org.foreningsgatan.modules.glcore.graphics.BlendMode;
import org.foreningsgatan.modules.glcore.graphics.GLGraphics;
import org.foreningsgatan.modules.glcore.graphics.transform.ShearMatrix;
import org.foreningsgatan.modules.glcore.graphics.transform.TransformMatrix;
import org.foreningsgatan.modules.glcore.view.GLApplication;

/**
 * Copyright (c) 2008 Joakim Back
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 * 
 * @author Joakim Back
 * 
 */
public class VegetationRenderer implements Renderer {
	private List<Vegetation> mVisibleVegetation;

	private ShadowGenerator mShadowGenerator;

	private Frames mShadowFrame;

	private LightMap mLightMap;

	private LightSources<LightSource> mLightSources;

	private WindController mWindController;

	private Log mLog = new Log(this.getClass());

	public VegetationRenderer(Frames pShadowFrame) {
		this.mVisibleVegetation = new ArrayList<Vegetation>();
		this.mShadowFrame = pShadowFrame;
		this.mShadowGenerator = new ShadowGenerator(Modes.AVERAGE, 0, 0.5);
		this.mWindController = new WindController();
	}

	public void update(LightSources<LightSource> pLightSources,
			LightMap pLightMap) {
		this.mLightSources = pLightSources;
		this.mLightMap = pLightMap;
	}

	private void renderShadow(GLGraphics g, Shadow vShadow, ScrollPosition pPos) {
		if (vShadow == null) {
			return;
		}

		double vAngle = vShadow.getAngle();

		Vegetation vVegetation = (Vegetation) vShadow.getObstacle();

		float x = RenderUtils.toScreenCoord(vShadow.getObstacle().getX(),
				Orientation.HORIZONTAL, pPos)
				+ (float) (Math.cos(vAngle) * Configuration.sShadowDistance * (float) vVegetation
						.getScale());
		float y = RenderUtils.toScreenCoord(vShadow.getObstacle().getY(),
				Orientation.VERTICAL, pPos)
				+ (float) (Math.sin(vAngle) * Configuration.sShadowDistance * (float) vVegetation
						.getScale()) / Configuration.sDepthFactor;

		float[] vRGB = new float[] { 1.0f, 1.0f, 1.0f,
				(float) vShadow.getIntensity() };

		g.drawFrames(x, y, mShadowFrame, Alignment.CENTER, (float) vVegetation
				.getScale() * 2, vRGB, BlendMode.ALPHA);
	}
	
	public void render(GLGraphics g, TileClippingRect pRect,
			ScrollPosition pPos, boolean hasScrolled) {
		// Update Visible Vegetation

		if (hasScrolled) {
			Timer t = new Timer();
			
			double vUpperLeftX = 
				RenderUtils.toWorldCoord(0, Orientation.HORIZONTAL, pPos)
				- Configuration.sVegetationMargin;
			double vUpperLeftY = 
				RenderUtils.toWorldCoord(0, Orientation.VERTICAL, pPos)
				- Configuration.sVegetationMargin;
			double vLowerRightX = 
				RenderUtils.toWorldCoord(g.getWidth(), Orientation.HORIZONTAL, pPos)
				+ Configuration.sVegetationMargin;
			double vLowerRightY = 
				RenderUtils.toWorldCoord(g.getHeight(), Orientation.VERTICAL, pPos)
				+ Configuration.sVegetationMargin;
			
			mVisibleVegetation = 
				VegetationCache.getInstance().getVegetation(vUpperLeftX, vUpperLeftY, vLowerRightX, vLowerRightY);
			
			if (LoggingUtils.isActive) {
				mLog.debug("Size of vegetation: %s", mVisibleVegetation.size());
				mLog.debug("Calculating vegetation took %s", t);
			}
		}

		// Generate Shadows

		List<Shadow> mShadows = mShadowGenerator.render(
				mLightSources
						.getLightSources(pRect.getVerticalHeight()
								+ Configuration.sFrameLightMapMargin, pRect
								.getHorizontalFirst() - 1, pRect
								.getVerticalFirst() - 1), mVisibleVegetation);

		// Render Shadows
		
		Timer t = new Timer();

		for (Shadow mShadow : mShadows) {
			renderShadow(g, mShadow, pPos);
		}
		
		if (LoggingUtils.isActive) {
			mLog.debug("Shadows took %s ms", t);
		}

		// Render Vegetation

		t = new Timer();
		
		int i = 0;
		for (Vegetation v : mVisibleVegetation) {
			i++;
			float x = RenderUtils.toScreenCoord(v.getX(),
					Orientation.HORIZONTAL, pPos);
			float y = RenderUtils.toScreenCoord(v.getY(), Orientation.VERTICAL,
					pPos);

			RGB rgb = mLightMap.get((int) v.getX(), (int) v.getY());

			TransformMatrix vMatrix = null;

			if (v.isAffectedByWind()) {
				double vIndex = (double) GLApplication.getCurrentFrame()
						/ (Configuration.sFramesPerSecond * Configuration.sWindRate)
						+ (v.getRandomNumber() * Configuration.sWindUniqueOffset)
						+ (v.getX() * Configuration.sWindHorizontalOffset);

				float vOffset = Configuration.sWindStrength
						* ((float) mWindController.get(vIndex) - 0.50f);

				vMatrix = new ShearMatrix(vOffset, 0.0f);
			}
			
			g.drawFrames(x, y + 16, v.getFrames(), Alignment.LOWER_MIDDLE,
					(float) v.getScale(), rgb.getRGB(), BlendMode.ALPHA,
					vMatrix);
		}
		
		if (LoggingUtils.isActive) {
			mLog.debug("Draw vegetation %s ms", t);
		}
	}

}
